import numpy as np
import cv2 as cv
from opencv.img_utils import get_rgb_img, show_two_imgs
import matplotlib.pyplot as plt

file_name = 'img/girl.png'


# 均值滤波
def mean_filter(src, k_size):
    '该方法就是遍历每个像素点，将给定的范围矩阵的均值，赋给当前像素'
    dst = np.zeros(src.shape, dtype=np.uint8)
    h, w = src.shape[0], src.shape[1]
    # python中 /为浮点除法，返回浮点数  //为整数除法，返回一个不大于结果的最大整数
    rx = k_size[0] // 2
    ry = k_size[1] // 2
    nc = src.shape[-1]
    for y in range(h):
        for x in range(w):
            n = 0
            v = np.zeros(nc, dtype=np.float32)
            for i in range(-ry, ry + 1):
                iy = i + y
                if iy < 0 or iy >= h:
                    continue
                for j in range(-rx, rx + 1):
                    jx = j + x
                    if jx < 0 or jx >= w:
                        continue
                    v += src[iy][jx]
                    n += 1
            v = v / n
            dst[y][x] = v.astype(np.uint8)

    return dst


def select_mean_filter(src, k_size, threshold):
    # 该方法就是遍历每个像素点，如果当前像素值与均值差值大于给定阈值threshold时
    # 将给定的范围矩阵的均值，赋给当前像素
    # 否则继续使用原像素值
    dst = np.zeros(src.shape, dtype=np.uint8)
    h, w = src.shape[0], src.shape[1]
    # python中 /为浮点除法，返回浮点数  //为整数除法，返回一个不大于结果的最大整数
    rx = k_size[0] // 2
    ry = k_size[1] // 2
    nc = src.shape[-1]
    for y in range(h):
        for x in range(w):
            n = 0
            v = np.zeros(nc, dtype=np.float32)
            for i in range(-ry, ry + 1):
                iy = i + y
                if iy < 0 or iy >= h:
                    continue
                for j in range(-rx, rx + 1):
                    jx = j + x
                    if jx < 0 or jx >= w:
                        continue
                    v += src[iy][jx]
                    n += 1
            v = (v / n).astype(np.uint8)
            mask = v - src[y][x] < threshold
            # todo 这句代表的意思
            v[mask] = src[y][x][mask]
            dst[y][x] = v

    return dst


def official_mean_filter():
    """使用filter2D来实现均值滤波，均值滤波可用于模糊图片，也可用于消除噪点，但是对于边缘也被模糊了"""
    kernel = np.ones((5, 5), np.float32) / 25
    img = get_rgb_img(file_name)
    dst = cv.filter2D(img, -1, kernel)
    plt.subplot(121), plt.imshow(img), plt.title('Original')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122), plt.imshow(dst), plt.title('Averaging')
    plt.xticks([]), plt.yticks([])
    plt.show()


def official_blur_mean():
    """均值模糊"""
    img = get_rgb_img(file_name)
    dst = cv.blur(img, (3, 3))
    plt.subplot(121), plt.imshow(img), plt.title('Original')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122), plt.imshow(dst), plt.title('blur_mean')
    plt.xticks([]), plt.yticks([])
    plt.show()


def official_blur_GaussianBlur():
    """高斯模糊，去除高斯噪声比较有效，内核大小为正奇数"""
    img = get_rgb_img(file_name)
    dst = cv.GaussianBlur(img, (5, 5), 0)
    plt.subplot(121), plt.imshow(img), plt.title('Original')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122), plt.imshow(dst), plt.title('GaussianBlur')
    plt.xticks([]), plt.yticks([])
    plt.show()


def official_blur_medianBlur():
    """中值模糊，去除椒盐噪声比较有效，内核大小为正奇数，原理为取内核区域的中间值替换核心值"""
    img = get_rgb_img(file_name)
    dst = cv.medianBlur(img, 5)
    plt.subplot(121), plt.imshow(img), plt.title('Original')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122), plt.imshow(dst), plt.title('GaussianBlur')
    plt.xticks([]), plt.yticks([])
    plt.show()


def official_blur_bilateralFilter():
    """双边滤波，保留边缘的强度"""
    img = get_rgb_img(file_name)
    dst = cv.bilateralFilter(img, 9, 75, 75)
    plt.subplot(121), plt.imshow(img), plt.title('Original')
    plt.xticks([]), plt.yticks([])
    plt.subplot(122), plt.imshow(dst), plt.title('GaussianBlur')
    plt.xticks([]), plt.yticks([])
    plt.show()


def test():
    src = get_rgb_img(file_name)
    # dst = mean_filter(get_rgb_img(file_name), (4, 4))
    dst = select_mean_filter(get_rgb_img(file_name), (4, 4), 10)
    show_two_imgs(src, 'src', dst, 'dst')


# official_mean_filter()
# official_blur_mean()
# official_blur_GaussianBlur()
# official_blur_medianBlur()
official_blur_bilateralFilter()
